﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Trentacular.SharePoint.Features.Util;
using Trentacular.SharePoint.Features.Web.UI.WebControls;

namespace Trentacular.SharePoint.Features.Web.UI.WebParts
{
    [Guid("11EA2D84-1CB1-4845-A80C-74AB77A89042")]
    [XmlRoot(Namespace = "Trentacular.SharePoint.Features.Web.UI.WebParts.ListViewWebPart")]
    public class ListViewWebPart : BaseSPWebPart
    {
        public ListViewWebPart()
        {
            RegisterEditorPart("ListSelector", "List View Configuration", "~/_layouts/trentacular/ListSelector.ascx");
            this.AllowEdit = false;
        }

        #region Stateful Properties

        [Personalizable(PersonalizationScope.Shared)]
        public bool IsRemoteList { get; set; }

        [Personalizable(PersonalizationScope.Shared)]
        public Guid? RemoteSiteId { get; set; }

        [Personalizable(PersonalizationScope.Shared)]
        public Guid? RemoteWebId { get; set; }

        [Personalizable(PersonalizationScope.Shared)]
        public Guid? ListId { get; set; }

        [Personalizable(PersonalizationScope.Shared)]
        public Guid? DefaultViewId { get; set; }

        #endregion

        private string _selectedViewQueryStringtKey;
        private string SelectedViewQueryStringKey
        {
            get
            {
                if (_selectedViewQueryStringtKey == null)
                    _selectedViewQueryStringtKey = string.Format("{0}_View", ID);
                return _selectedViewQueryStringtKey;
            }
        }

        public static bool IsViewAvailableForSelection(SPView view)
        {
            if (view.Hidden)
                return false;

            if (view.ParentList.ContentTypesEnabled)
                return view.ContentTypeId.Is(SPBuiltInContentTypeId.System, SPBuiltInContentTypeId.RootOfList);

            return true;
        }

        private SPView GetView(SPList list)
        {
            var strViewId = Context.Request.QueryString[SelectedViewQueryStringKey];
            var viewId = string.IsNullOrEmpty(strViewId) ? DefaultViewId : new Guid(strViewId);

            // Validate the selected view exists in the list
            if (viewId.HasValue && list.Views.Cast<SPView>().FirstOrDefault(v => v.ID.Equals(viewId.Value)) != null)
            {
                var view = list.Views[viewId.Value];
                if (IsViewAvailableForSelection(view))
                    return view;
            }

            return list.DefaultView;
        }

        private const string ListViewWebPartExistsKey = "ListViewWebPartExists";
        private bool ListViewWebPartExists
        {
            get
            {
                return Page != null && Page.Items.Contains(ListViewWebPartExistsKey) && (bool)Page.Items[ListViewWebPartExistsKey];
            }
            set
            {
                if (Page != null)
                    Page.Items[ListViewWebPartExistsKey] = value;
            }
        }

        private bool ValidateState()
        {
            if (!ListId.HasValue)
            {
                Controls.Add(new Literal { Text = string.Format("Choose a list to display from the {0}.", GetToolPaneLink("tool pane")) });
                return false;
            }

            // Ensure this is the only webpart of this type on the page since this
            // webpart doesn't play nice with mulitple occurrences
            if (ListViewWebPartExists)
            {
                Controls.Add(new Literal { Text = string.Format("Only one webpart of type '{0}' may be rendered per page", GetType().FullName) });
                return false;
            }

            return true;
        }

        protected override void RenderContentsHandler(HtmlTextWriter writer)
        {
            if (!ValidateState())
            {
                base.RenderContentsHandler(writer);
                return;
            }

            if (IsRemoteList)
            {
                using (var site = new SPSite(RemoteSiteId.Value))
                {
                    using (var web = site.OpenWeb(RemoteWebId.Value))
                    {
                        RenderContents(web, writer);
                    }
                }
            }
            else
            {
                RenderContents(Web, writer);
            }

            ListViewWebPartExists = true;
        }

        private void RenderContents(SPWeb web, HtmlTextWriter writer)
        {
            var list = web.Lists[ListId.Value];
            var view = GetView(list);

            BuildControls(view);
            base.RenderContentsHandler(writer);
            RenderGrid(view, writer);
        }

        private void BuildControls(SPView view)
        {
            var toolbar = IsRemoteList ? (TemplateBasedControl)new RemoteListViewToolBar() : new ViewToolBar();
            toolbar.RenderContext = SPContext.GetContext(this.Context, view.ID, view.ParentList.ID, view.ParentList.ParentWeb);

            Controls.Add(toolbar);

            var toolbarTable = toolbar.TemplateContainer.FindControl("toolBarTbl");
            if (toolbarTable == null)
                return;

            var rightRptControls = toolbarTable.FindControl("RightRptControls");
            if (rightRptControls == null)
                return;

            var listViewSelector = rightRptControls.Controls.OfType<ListViewSelector>().FirstOrDefault();
            if (listViewSelector == null)
                return;

            var tdViewSelector = listViewSelector.TemplateContainer.FindControl("onetViewSelector");
            if (tdViewSelector == null)
                return;

            // Remove the existing ViewSelectorMenu control
            tdViewSelector.Controls.Clear();
            tdViewSelector.Controls.Add(new SelfRedirectingViewSelector
            {
                ViewQueryStringKey = SelectedViewQueryStringKey,
                MenuAlignment = MenuAlignment.Right,
                AlignToParent = true
            });
        }

        protected virtual void RenderGrid(SPView view, HtmlTextWriter writer)
        {
            var web = view.ParentList.ParentWeb;
            var file = web.GetFile(view.Url);
            using (var webPartMan = file.GetLimitedWebPartManager(PersonalizationScope.Shared))
            {
                var remoteWebPart = webPartMan.WebParts.OfType<Microsoft.SharePoint.WebPartPages.ListViewWebPart>().FirstOrDefault();

                // Now we are going to invoke the private RenderView() method of the ListViewWebPart
                var method = remoteWebPart.GetType().GetMethod("RenderView",
                    BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    Type.EmptyTypes,
                    null
                    );

                var viewContents = method.Invoke(remoteWebPart, new object[] { });
                var viewHtml = new StringBuilder(viewContents is char[] ? new string((char[])viewContents) : viewContents.ToString());

                var absolutePathEncodedViewUrl = HttpUtility.UrlPathEncode(string.Format("{0}/{1}", web.Url.TrimEnd('/'), view.Url));
                var currentPageEncodedUrl = HttpUtility.UrlPathEncode(Context.Request.Path);

                // Need to fix Folder Links by making them relative to the current page
                var folderLinkSearchTemplate = " HREF=\"{0}?RootFolder=";
                viewHtml.Replace(
                    string.Format(folderLinkSearchTemplate, absolutePathEncodedViewUrl),
                    string.Format(folderLinkSearchTemplate, currentPageEncodedUrl)
                    );

                // Need to fix folder link onclick scripts
                var folderLinkClickSearchTemplate = " onclick=\"javascript:EnterFolder('{0}?RootFolder=";
                viewHtml.Replace(
                    string.Format(folderLinkClickSearchTemplate, SPEncode.ScriptEncode(absolutePathEncodedViewUrl)),
                    string.Format(folderLinkClickSearchTemplate, SPEncode.ScriptEncode(currentPageEncodedUrl))
                    );

                writer.WriteLine(viewHtml.ToString());
            }
        }
    }
}
